/*
Copyright 2010 Bryce Alcock

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.com

 */


package com.ecocrypt.proxy.forwarder;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author bryce
 */
public class RequestSerializationBuffer extends Thread {

    int FileRecreateCount = 100000;
    Forwarder f;
    final private LinkedBlockingQueue<ClientServiceRequest> reqList = new LinkedBlockingQueue<ClientServiceRequest>(1000);

    // Potential memory leak when using the oos.
    // These streams have to be resent periodically.
    // in this case we will close every 100000 records.
    // however, that may be too long between resets.
    ObjectOutputStream oos;
    PrintWriter pw;
    int currentCount;

    public RequestSerializationBuffer(Forwarder fwdr) {

	f=fwdr;
	this.setDaemon(true);
	
    }

    public void startThread(){

	this.setName("[Persistance Thread]"+f.toString());
	this.start();
	
    }


    public void add(ClientServiceRequest csr) {
        if(ForwardProxy.isProd){
            // NO LOGGING TO  A FILE WILL BE DONE.
            // In order to do this you will have to select a non-prod mode.
            return;
        }
	if(!this.isAlive()){
	    synchronized(this){
		if(!this.isAlive()){
		    this.startThread();
		}
	    }
	}
        if(!reqList.offer(csr)){
            // Log this somewhere:
            System.err.println("Failed to Offer CSR to RequestSerialization Buffer.  : Size : "
                    + reqList.size()+" Thread state: "
                    + String.valueOf(this.getState().name())+" and to string: "
                    + String.valueOf(this.getState()));
        }
    }

    @Override
    public void run() {
	while (true) {
	    try {
		ClientServiceRequest csr = reqList.take();
                if(ForwardProxy.textOutput){
                    serializeToDisk(csr.toString());
                }else{
                    serializeToDisk(csr);
                }
	    } catch (Throwable ex) {
		ex.printStackTrace(System.out);
		Logger.getLogger(RequestSerializationBuffer.class.getName()).log(Level.SEVERE, null, ex);
	    }
	}
    }

    private void serializeToDisk(String s){
        ++currentCount;
	if (pw != null && currentCount <= FileRecreateCount) {
            pw.println(s);
	} else {
	    currentCount=0;

	    if (pw != null) {
		    pw.flush();
		    pw.close();
		    pw = null;
	    }
	    String fString = f.toString();
	    fString = fString.replaceAll(":", "-");
            SimpleDateFormat sdf = new SimpleDateFormat("MMddHHmm");
	    fString += "_seq-" + sdf.format(new Date()) + ".txt";
	    File outFile = new File(fString);
	    if (!outFile.exists()) {
		try {
		    outFile.createNewFile();
		} catch (IOException ex) {
		    ex.printStackTrace(System.out);
		}
	    }
	    try {
		FileOutputStream fos = new FileOutputStream(outFile);
		BufferedOutputStream bos = new BufferedOutputStream(fos, 1024 * 8);
                if(ForwardProxy.textOutput){
                    pw= new PrintWriter(bos);
                }else{
                    throw new IllegalStateException(" Should not get to Binary format for this method.");
                }

	    } catch (FileNotFoundException ex) {
		ex.printStackTrace(System.out);
		Logger.getLogger(RequestSerializationBuffer.class.getName()).log(Level.SEVERE, null, ex);
	    } catch (IOException ex) {
		ex.printStackTrace(System.out);
	    }
	    if (pw != null) {
               pw.println(s);
	    }
	}

    }
    private void serializeToDisk(ClientServiceRequest csr) {
	++currentCount;
	if (oos != null && currentCount <= FileRecreateCount) {
	    try {
		oos.writeObject(csr);
		if(currentCount%500==0){
		    // This will prevent an out of memory error while serializing
		    // data out to file.
		    // The choise of 500 is random, but this is a detail that
		    // would be too much for a end user configuration.
		    // CONFIGURATION OPTION: Output buffer stream reset frequency in RequestSerializationBuffer.
		    oos.flush();
		    oos.reset();
		}
	    } catch (IOException ex) {
		// try to close and reset the file.
		try{
		    //  without a reset, you are at the mercy of the GC to clean
		    //  up backwards references to objects.
		    oos.reset();
		    oos.close();
		}catch(IOException io){

		}
		oos = null;
		ex.printStackTrace(System.out);
		Logger.getLogger(RequestSerializationBuffer.class.getName()).log(Level.SEVERE, null, ex);
	    }
	} else {
	    currentCount=0;

	    if (oos != null) {
		try {
		    oos.flush();
		    oos.close();
		    oos = null;
		} catch (IOException ex) {
		    ex.printStackTrace(System.out);
		    Logger.getLogger(RequestSerializationBuffer.class.getName()).log(Level.SEVERE, null, ex);
		}
	    }
	    String fString = f.toString();
	    fString = fString.replaceAll(":", "-");
	    fString += "_seq-" + csr.serviceId.intValue() + ".bla";
	    File outFile = new File(fString);
	    if (!outFile.exists()) {
		try {
		    outFile.createNewFile();
		} catch (IOException ex) {
		    ex.printStackTrace(System.out);
		}
	    }
	    try {
		FileOutputStream fos = new FileOutputStream(outFile);
		BufferedOutputStream bos = new BufferedOutputStream(fos, 1024 * 8);
                if(ForwardProxy.textOutput){
                    pw= new PrintWriter(bos);
                }else{
                    oos = new ObjectOutputStream(bos);
                }

	    } catch (FileNotFoundException ex) {
		ex.printStackTrace(System.out);
		Logger.getLogger(RequestSerializationBuffer.class.getName()).log(Level.SEVERE, null, ex);
	    } catch (IOException ex) {
		ex.printStackTrace(System.out);
	    }
	    if (oos != null) {
		try {
		    oos.writeObject(csr);
		} catch (IOException ex) {
		    oos = null;
		    ex.printStackTrace(System.out);
		    Logger.getLogger(RequestSerializationBuffer.class.getName()).log(Level.SEVERE, null, ex);
		}
	    }
	}
    }
}
